/*
 * #%L
 * Client
 * %%
 * Copyright (C) 2011 Ractoc.com
 * %%
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * #L%
 */
package com.ractoc.opengamefinder.client.plugins;

import java.io.IOException;

import org.apache.log4j.Logger;

import com.jme3.network.Client;
import com.jme3.network.ErrorListener;
import com.jme3.network.Message;
import com.jme3.network.MessageListener;
import com.jme3.network.Network;
import com.ractoc.opengamefinder.api.results.MessageResultsErrorCodes;
import com.ractoc.opengamefinder.api.messages.NetworkMessage;
import com.ractoc.opengamefinder.api.network.ClientServerMessage;
import com.ractoc.opengamefinder.api.plugins.BaseSpiderMonkeyPlugin;
import com.ractoc.opengamefinder.api.results.NetworkMessageResult;
import com.ractoc.opengamefinder.api.translators.TranslatorMap;
import com.ractoc.pffj.api.BasePluginMessage;
import com.ractoc.pffj.api.BasePluginMessageResult;
import com.ractoc.pffj.api.PluginLoaderException;
import com.ractoc.pffj.api.PluginMessage;
import com.ractoc.pffj.api.PluginMessageResultListener;
import com.ractoc.pffj.core.PluginController;
import com.ractoc.pffj.core.PluginException;

/**
 * Network plugin using the SpiderMonkey network technology currently built into
 * JME3.
 * 
 * @author Mark
 * @version 0.1
 */
public class SpiderMonkeyPlugin extends BaseSpiderMonkeyPlugin implements ErrorListener<Client>, MessageListener<Client> {

    private static Logger logger = Logger.getLogger(SpiderMonkeyPlugin.class.getName());
    private String serverIp;
    private int serverTcpPort;
    private int serverUdpPort;

    /**
     * Default constructor.
     */
    public SpiderMonkeyPlugin() {
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public final void load() throws PluginLoaderException {
        try {
            registerClasses();

            logger.debug("starting client on " + serverIp + ":" + serverTcpPort + ":" + serverUdpPort);
            client = Network.connectToServer(serverIp, serverTcpPort, serverUdpPort);
            client.addMessageListener(this);
            client.addErrorListener(this);
            client.start();
            logger.debug("started client");
        }
        catch (ClassNotFoundException ex) {
            logger.error("unable to serialize all classes", ex);
        }
        catch (final IOException e) {
            logger.error("Unable to open a connection to the server.", e);
        }
    }

    /**
     * {@inheritDoc}
     * Currently supports only one Message, NetworkMessage.
     */
    @Override
    public final void processMessage(final PluginMessage msg, final PluginMessageResultListener pmrListener) {
        // Here we process the messages to send to the server.
        BasePluginMessageResult result = null;
        if (msg instanceof NetworkMessage) {
            final NetworkMessage nMsg = (NetworkMessage) msg;
            // here we send the payload to the server...
            final BasePluginMessage pMsg = nMsg.getPayload();
            final NetworkMessageResult nResult = new NetworkMessageResult();
            nResult.setErrorCode(MessageResultsErrorCodes.SUCCESS);
            try {
                sendMessage(pMsg, nMsg.getOriginPlugin(), nMsg.getDestinationPlugin(), nMsg.isReliable(), nMsg.isSecure(), null);
            }
            catch (final IOException e) {
                nResult.setErrorCode(MessageResultsErrorCodes.INTERNAL_ERROR);
            }
            result = nResult;
        }
        pmrListener.receivedResult(result);
    }

    /**
     * {@inheritDoc}
     * Currently supports only one Message: ClientServerMessage. 
     * All other network messages should therefor extend this base Message.
     */
    @Override
    public final void messageReceived(final Client source, final Message message) {
        logger.debug("message received: " + message.getClass().getName());
        try {
            if (message instanceof ClientServerMessage) {
                final String destinationPlugin = ( (ClientServerMessage) message ).getDestinationPlugin();
                logger.debug("destinationPlugin=" + destinationPlugin);
                final BasePluginMessage msg = (BasePluginMessage) TranslatorMap.translate(message);
                logger.debug("message class: " + msg.getClass().getName());
                if (msg instanceof BasePluginMessageResult) {
                    logger.debug("result error code: " + ( (BasePluginMessageResult) msg ).getErrorCode());
                }
                try {
                    PluginController.getCurrentPluginController().processMessage(destinationPlugin, msg);
                }
                catch (final PluginException e) {
                    logger.error("Error processing plugin message", e);
                }
            }
        }
        catch (Throwable t) {
            logger.error("Error processing network message", t);
        }
    }

    /**
     * Set the ip address of the server. This is done from the PFfJ config file.
     * @param serverIp The server ip address.
     */
    public void setServerIp(String serverIp) {
        this.serverIp = serverIp;
    }

    /**
     * Returns the ip address as configured from the PFfJ config file.
     * @return The server ip address.
     */
    public String getServerIp() {
        return serverIp;
    }

    /**
     * Sets the TCP port of the server as configured from the PFfJ config file.
     * @param serverTcpPort The TCP server port.
     */
    public void setServerTcpPort(int serverTcpPort) {
        this.serverTcpPort = serverTcpPort;
    }

    /**
     * Returns the TCP port as configured from the PFfJ config file.
     * @return The server TCP port.
     */
    public int getServerTcpPort() {
        return serverTcpPort;
    }

    /**
     * Sets the UDP port of the server as configured from the PFfJ config file.
     * @param serverUdpPort The UDP server port.
     */
    public void setServerUdpPort(int serverUdpPort) {
        this.serverUdpPort = serverUdpPort;
    }

    /**
     * Returns the UDP port as configured from the PFfJ config file.
     * @return The server UDP port.
     */
    public int getServerUdpPort() {
        return serverUdpPort;
    }

    @Override
    public void handleError(Client s, Throwable thrwbl) {
        logger.error("Error on the connection to the server, unable to recover so closing the client.", thrwbl);
        System.exit(MessageResultsErrorCodes.NETWORK_ERROR.ordinal());
    }
}
